#include "company.h"

using namespace rational;

bool operator>(const myPair<Rational, int>& r1, const myPair<Rational, int>& r2)
{
	if (r1.first!=r2.first)
	{
		return r1.first>r2.first;
	}
	else
	{
		return r1.second>r2.second;
	}
}

bool operator==(const myPair<Rational, int>& r1, const myPair<Rational, int>& r2)
{
	return r1.first==r2.first && r1.second==r2.second;
}

Company::Company(int id)
	: _SalaryCoefficient(1), _Id(id)
{
}

void Company::HireEmployee(shared_ptr<Employee> empPtr)
{
	if (empPtr->GetSalary()<=0)
	{
		throw InvalidSalary();
	}
	Rational FixedSalary = empPtr->GetSalary() / _SalaryCoefficient;
	myPair<Rational, int> key(FixedSalary, empPtr->GetId());
	empPtr->SetSalary(FixedSalary);
	empPtr->SetEmployer(_Id);

	_Employees.InsertKey(key, empPtr);
	UpdateMedAndMax();
}

void Company::FireEmployee(int employeeId, const Rational &salary)
{
	myPair<Rational, int> key(salary, employeeId);
	_Employees.DeleteKey(key);
	UpdateMedAndMax();
}

void Company::UpdateMedAndMax()
{
	int numberOfEmployees = _Employees.Size();
	if (numberOfEmployees>0)
	{
		int mid = (numberOfEmployees-1)/2 + 1;
		_MedianSalary = _Employees.Select(mid).first.first;
		_MaxSalary = _Employees.Select(numberOfEmployees).first.first;
	}
}

Rational Company::GetMedianSalary() const
{
	if (this->_Employees.Size()==0)
	{
		throw NoEmployees();
	}
	return _MedianSalary * _SalaryCoefficient;
}

Rational Company::GetMaxSalary() const
{
	if (this->_Employees.Size()==0)
	{
		throw NoEmployees();
	}
	return _MaxSalary * _SalaryCoefficient;
}

void Company::UpdateSalary(const rational::Rational &coefficient)
{
	if (coefficient>0)
	{
		_SalaryCoefficient *= coefficient;
	}
	else
	{
		throw InvalidSalary();
	}
}

Rational Company::GetRealSalary(const Employee& employee) const
{
	if (employee.GetEmployer() != _Id)
	{
		throw EmployeeNotInCompany();
	}
	return employee.GetSalary() * _SalaryCoefficient;
}

void Company::MergeVectors(const SalaryEmployeeVector& vec1,
						   const SalaryEmployeeVector& vec2,
						   SalaryEmployeeVector& res)
{
	int currIdx=0, otherIdx=0;
	int v1Size = vec1.Size(), v2Size = vec2.Size();
	while (currIdx<v1Size || otherIdx<v2Size)
	{
		if (currIdx<v1Size && otherIdx<v2Size)
		{
			if (vec2[otherIdx].first > vec1[currIdx].first)
			{
				res.push_back(vec1[currIdx]);
				++currIdx;
			}
			else if (vec1[currIdx].first > vec2[otherIdx].first)
			{
				res.push_back(vec2[otherIdx]);
				++otherIdx;
			}
			else
			{
				throw KeyAlreadyInTreeException();
			}
		}
		else if (currIdx<v1Size)
		{
			res.push_back(vec1[currIdx]);
			++currIdx;
		}
		else
		{
			res.push_back(vec2[otherIdx]);
			++otherIdx;
		}
	}
}

void Company::NormalizeSalaries(Company& comp, Company::SalaryEmployeeVector& vec)
{
	for (int i=0; i<vec.Size(); ++i)
	{
		vec[i].first.first *= comp._SalaryCoefficient;
		vec[i].second->SetSalary(vec[i].second->GetSalary() * comp._SalaryCoefficient);
	}
	comp._SalaryCoefficient = 1;
}

void Company::Merge(Company& other)
{
	if (this == &other)
	{
		throw InvalidInputExcpetion();
	}

	SalaryEmployeeVector currData;
	_Employees.FlattenTree(currData);
	NormalizeSalaries(*this, currData);

	SalaryEmployeeVector otherData;
	other._Employees.FlattenTree(otherData);
	NormalizeSalaries(other, otherData);
	for (int i=0; i<otherData.Size(); ++i)
	{
		otherData[i].second->SetEmployer(_Id);
	}
	other._Employees.Clear();

	SalaryEmployeeVector mergedData;
	mergedData.reserve(_Employees.Size() + other._Employees.Size());
	MergeVectors(currData, otherData, mergedData);
	_Employees.BuildTreeFromVector(mergedData);

	if (mergedData.Size()>0)
	{
		_MedianSalary = mergedData[(mergedData.Size()-1)/2].first.first;
		_MaxSalary = mergedData[mergedData.Size()-1].first.first;
	}
}
